package com.lcf.framework.permission;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lcf.framework.enums.RedisCachePrefixEnum;
import com.lcf.framework.utils.RedisCacheUtils;
import com.lcf.framework.utils.UserUtils;
import com.lcf.system.entity.SysMenu;
import com.lcf.system.entity.SysRole;
import com.lcf.system.entity.SysRoleMenu;
import com.lcf.system.entity.SysUserRole;
import com.lcf.system.enums.SysMenuUseEnum;
import com.lcf.system.enums.SysRoleEnum;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 描述：自定义权限验证接口扩展
 *
 * @author lcf
 * @since 2025/2/21 23:48
 */
@Component
public class StpInterfaceImpl implements StpInterface {
    @Resource
    private RedisCacheUtils redisCacheUtils;

    private static final int CACHE_TIME = 5;
    private static final TimeUnit CACHE_UNIT = TimeUnit.MINUTES;

    /**
     * 返回此 loginId 拥有的权限列表
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        if (UserUtils.isPlatformAdmin()) {
            return List.of("*");
        }

        //角色编码列表
        List<String> roleCodeList = getRoleList(loginId, loginType);
        if (CollectionUtil.isEmpty(roleCodeList)) {
            return Collections.emptyList();
        }

        //租户管理员则返回全部租户权限
        if (roleCodeList.stream().anyMatch(x -> Objects.equals(x, SysRoleEnum.ADMIN.getCode()))) {
            SysRole adminRole = Db.lambdaQuery(SysRole.class)
                    .eq(SysRole::getRoleCode, SysRoleEnum.ADMIN.getCode())
                    .one();
            if (adminRole != null) {
                String key = RedisCachePrefixEnum.ROLE_PERMISSION.getKey(adminRole.getId());
                List<String> cache = redisCacheUtils.getList(key);
                if (CollectionUtil.isNotEmpty(cache)) {
                    return cache;
                }

                List<String> permissionList = Db.lambdaQuery(SysMenu.class)
                        .eq(SysMenu::getMenuUse, SysMenuUseEnum.TENANT.getCode())
                        .list().stream()
                        .filter(x -> StringUtils.isNotBlank(x.getPermissionCodes()))
                        .map(x -> Arrays.stream(x.getPermissionCodes().split(",")).map(String::trim).toList())
                        .flatMap(List::stream).toList();

                redisCacheUtils.cacheList(key, permissionList);
                redisCacheUtils.expire(key, CACHE_TIME, CACHE_UNIT);

                return permissionList;
            }
        }

        //权限列表
        List<String> resultList = new ArrayList<>();
        //根据角色编码获取角色id
        List<Long> roleIdList = getRoleIdList(loginId, roleCodeList);

        //遍历角色编码列表，获取其权限列表
        roleIdList.forEach(roleId -> {
            String key = RedisCachePrefixEnum.ROLE_PERMISSION.getKey(roleId);

            List<String> cache = redisCacheUtils.getList(key);
            if (CollectionUtil.isNotEmpty(cache)) {
                resultList.addAll(cache);
                return;
            }

            //根据角色编码去查询菜单id列表
            List<Long> menuIdList = Db.lambdaQuery(SysRoleMenu.class)
                    .eq(SysRoleMenu::getRoleId, roleId)
                    .list()
                    .stream()
                    .map(SysRoleMenu::getMenuId)
                    .toList();

            if (CollectionUtil.isEmpty(menuIdList)) {
                return;
            }

            //根据菜单id去查询菜单列表，获取权限列表
            List<String> permission = Db.lambdaQuery(SysMenu.class)
                    .in(SysMenu::getId, menuIdList)
                    .list()
                    .stream()
                    .filter(x -> StringUtils.isNotBlank(x.getPermissionCodes()))
                    .map(x -> Arrays.stream(x.getPermissionCodes().split(",")).map(String::trim).toList())
                    .flatMap(List::stream).toList();

            resultList.addAll(permission);

            redisCacheUtils.cacheList(key, permission);
            redisCacheUtils.expire(key, CACHE_TIME, CACHE_UNIT);
        });

        return resultList;
    }

    /**
     * 返回此 loginId 拥有的角色列表
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        if (UserUtils.isPlatformAdmin()) {
            return List.of("*");
        }

        long tenantId = UserUtils.getTenantId();
        String key = RedisCachePrefixEnum.USER_ROLE.getKey(loginId + ":" + tenantId);
        List<String> list = redisCacheUtils.getList(key);
        if (CollectionUtils.isNotEmpty(list)) {
            return list;
        }

        List<String> roleIds = Db.lambdaQuery(SysUserRole.class)
                .eq(SysUserRole::getUserId, loginId)
                .list()
                .stream()
                .map(x -> String.valueOf(x.getRoleId()))
                .toList();
        if (CollectionUtil.isEmpty(roleIds)) {
            redisCacheUtils.cacheList(key, Collections.emptyList());
            redisCacheUtils.expire(key, CACHE_TIME, CACHE_UNIT);

            return Collections.emptyList();
        }

        List<String> roleCodeList = Db.lambdaQuery(SysRole.class)
                .in(SysRole::getId, roleIds)
                .list()
                .stream()
                .map(SysRole::getRoleCode)
                .toList();

        redisCacheUtils.cacheList(key, roleCodeList);
        redisCacheUtils.expire(key, CACHE_TIME, CACHE_UNIT);

        return roleCodeList;
    }

    /**
     * 获取角色id列表
     */
    private List<Long> getRoleIdList(Object userId, List<String> roleCodeList) {
        String key = RedisCachePrefixEnum.USER_ROLE_ID.getKey(userId + ":" + UserUtils.getTenantId());
        List<Long> roleIdList = redisCacheUtils.getList(key);
        if (CollectionUtils.isNotEmpty(roleIdList)) {
            return roleIdList;
        }

        roleIdList = Db.lambdaQuery(SysRole.class)
                .select(SysRole::getId)
                .in(SysRole::getRoleCode, roleCodeList)
                .list()
                .stream().map(SysRole::getId).toList();

        redisCacheUtils.cacheList(key, roleIdList);
        redisCacheUtils.expire(key, CACHE_TIME, CACHE_UNIT);

        return roleIdList;
    }
}
